﻿using PBO.Data.SQLite.DAL;
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using PBO.Data.Util;

namespace PBO.Data
{
    public class PokemonCustomInfo : DataStream, ICloneable, IEquatable<PokemonCustomInfo>
    {

        #region null object

        public static readonly PokemonCustomInfo EmptyPokemon = new PokemonCustomInfo() { Identity = 0 };

        public bool IsEmpty
        {
            get
            {
                return this.Identity == 0;
            }
        }

        #endregion

        public PokemonCustomInfo()
        {
            this.Moves = Enumerable.Repeat<int>(0, 4).ToArray();
            this.Ev = new BaseStats(85);
            this.Iv = new BaseStats(31);
        }

        #region props

        public PokemonNature Nature { get; set; }
        public PokemonGender Gender { get; set; }
        public int Identity { get; set; }
        public Item Item { get; set; }
        public byte LV { get; set; }
        public string NickName { get; set; }
        public Trait Ability { get; set; }
        public BaseStats Ev { get; set; }
        public BaseStats Iv { get; set; }
        public int[] Moves { get; set; }

        #endregion

        public string GetKey()
        {
            var dic = new Dictionary<string, string>();
            dic.Add("pid", this.Identity.ToString());
            dic.Add("ability", ((int)this.Ability).ToString());
            dic.Add("item", ((int)this.Item).ToString());
            dic.Add("gender", ((int)this.Gender).ToString());
            dic.Add("nature", ((int)this.Nature).ToString());
            dic.Add("lvl", this.LV.ToString());
            var moves = Moves.Where(m => m > 0).Distinct().OrderBy(m => m);
            dic.Add("moves", string.Join(",", moves));
            dic.Add("evs", string.Join(",", this.Ev));
            dic.Add("dvs", string.Join(",", this.Iv));
            return StringHelper.SignTopRequest(dic, string.Empty, false);
        }

        #region ICloneable

        public PokemonCustomInfo Clone()
        {
            return (PokemonCustomInfo)this.MemberwiseClone();
        }

        object ICloneable.Clone()
        {
            return this.Clone();
        }

        #endregion

        #region IEquatable

        public bool Equals(PokemonCustomInfo other)
        {
            return this.Identity == other.Identity &&
                this.NickName == other.NickName &&
                this.LV == other.LV &&
                this.Ev.Equals(other.Ev) &&
                this.Iv.Equals(other.Iv) &&
                this.Gender == other.Gender &&
                this.Nature == other.Nature &&
                this.Ability == other.Ability &&
                this.Item == other.Item &&
                this.Moves[0] == other.Moves[0] &&
                this.Moves[1] == other.Moves[1] &&
                this.Moves[2] == other.Moves[2] &&
                this.Moves[3] == other.Moves[3];
        }

        #endregion

        #region Binary

        private static readonly StatType[] TYPES = { StatType.Atk, StatType.Def, StatType.Speed, StatType.SpAtk, StatType.SpDef, StatType.Hp };

        public override void BinaryLoad(BinaryReader reader)
        {
            int id = reader.ReadInt32();
            var data = GameService.GetPokemon(id);
            if (data == null) return;

            this.Identity = id;
            this.NickName = reader.ReadString();
            this.LV = reader.ReadByte();
            foreach (StatType stat in TYPES)
            {
                this.Ev[stat] = reader.ReadByte();
            }
            foreach (StatType stat in TYPES)
            {
                this.Iv[stat] = reader.ReadByte();
            }
            this.Gender = (PokemonGender)reader.ReadInt32();
            this.Ability = data.GetTrait(reader.ReadByte());
            this.Nature = (PokemonNature)reader.ReadInt32();
            this.Item = (Item)reader.ReadInt32();
            for (int i = 0; i < this.Moves.Length; i++)
            {
                string move = reader.ReadString();
                this.Moves[i] = DALMoveData.GetMoveIdByOldName(move);
            }
            //this.NickName = reader.ReadString();
            //this.LV = reader.ReadByte();
            //foreach (StatType stat in BaseStats.Types)
            //{
            //    this.Ev[stat] = reader.ReadByte();
            //}
            //foreach (StatType stat in BaseStats.Types)
            //{
            //    this.Iv[stat] = reader.ReadByte();
            //}
            //this.Gender = (PokemonGender)reader.ReadInt32();
            //this.Ability = (Trait)reader.ReadInt32();
            //this.Nature = (PokemonNature)reader.ReadInt32();
            //this.Item = (Item)reader.ReadInt32();

            //for (int i = 0; i < this.Moves.Length; i++)
            //{
            //    this.Moves[i] = reader.ReadInt32();
            //}
        }

        public override void BinarySave(BinaryWriter writer)
        {
            if (this.IsEmpty) return;
            writer.Write(Identity);
            writer.Write(NickName);
            writer.Write(LV);
            foreach (StatType stat in TYPES)
            {
                writer.Write((byte)this.Ev[stat]);
            }
            foreach (StatType stat in TYPES)
            {
                writer.Write((byte)this.Iv[stat]);
            }
            writer.Write((int)this.Gender);
            var data = GameService.GetPokemon(Identity);
            writer.Write(data.GetSelectedTrait(Ability));
            writer.Write((int)this.Nature);
            writer.Write((int)this.Item);
            foreach (var move_id in this.Moves)
            {
                writer.Write(move_id == 0 ? string.Empty : GameService.GetMove(move_id).OldName);
            }
            //writer.Write(this.Identity);
            //if (this.Identity != 0)
            //{
            //    writer.Write(this.NickName);
            //    writer.Write(this.LV);
            //    foreach (StatType stat in BaseStats.Types)
            //    {
            //        writer.Write((byte)this.Ev[stat]);
            //    }
            //    foreach (StatType stat in BaseStats.Types)
            //    {
            //        writer.Write((byte)this.Iv[stat]);
            //    }
            //    writer.Write((int)this.Gender);
            //    writer.Write((int)this.Ability);
            //    writer.Write((int)this.Nature);
            //    writer.Write((int)this.Item);
            //    foreach (int move in this.Moves)
            //    {
            //        writer.Write(move);
            //    }
            //}
        }

        #endregion

        #region move

        public bool AddMove(int mid)
        {
            if (this.Moves.All(i => i != 0) || this.Moves.Contains(mid)) return false;
            for (int i = 0; i < 4; i++)
            {
                if (this.Moves[i] == 0)
                {
                    this.Moves[i] = mid;
                    return true;
                }
            }
            return false;
        }

        public bool RemoveMove(int mid)
        {
            if (this.Moves.Count(i => i != 0) == 1 || !this.Moves.Contains(mid)) return false;
            for (int i = 0; i < 4; i++)
            {
                if (this.Moves[i] == mid)
                {
                    this.Moves[i] = 0;
                    return true;
                }
            }
            return false;
        }

        public void SortMoves()
        {
            var list = this.Moves.Where(i => i != 0).ToList();
            int count = 4 - list.Count;
            for (int i = 0; i < count; i++)
            {
                list.Add(0);
            }
            this.Moves = list.ToArray();
        }

        #endregion

    }
}
